package org.goodprinciples.commons.command;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.apache.log4j.Logger;
import org.goodprinciples.commons.command.exception.CommandException;
import org.goodprinciples.commons.command.exception.CommandNotProperlySetted;
import org.goodprinciples.commons.command.exception.CommandRuntimeException;
import org.goodprinciples.commons.util.log.GoodPrinciplesLoggerUtil;

/**
 *
 * @author Francesco Conte
 */
public class MultiCommand implements Command {
    
    /* LOGGERS */
    
    private static Logger logger = Logger.getLogger(MultiCommand.class);
    
    /* INSTANCE VARIABLES */
    
    private CommandInfo commandInfo = null;
    
    /* CONSTRUCTORS */

    public MultiCommand() {}
    
    /* ACCESSORS AND MUTATORS */
    
    @Override
    public CommandInfo getCommandInfo() {
        return commandInfo;
    }
    
    @Override
    public void setCommandInfo(CommandInfo commandId) {
        this.commandInfo = commandId;
    }

    /* BUSINESS METHODS */

    @Override
    public final void cancel() throws CommandException {
        throw new UnsupportedOperationException("Not supported yet.");
    }
    
    @Override
    public final void execute() throws CommandException {
        if(getCommandInfo().getUuid() == null && getCommandInfo().getExecutionModality() == null && getCommandInfo().getUser() == null) {
            /*
             * in questo caso non si può eseguire propriamente il comando in quanto
             * non si riuscirebbe ad eseguire nessun tracciamento e soprattutto non
             * si riuscirebbe a capire quale metodo eseguire
             */
            GoodPrinciplesLoggerUtil.fatal(logger, this, "The properties CommandId, ExecutionModality and User are not setted!!!");
            throw new CommandNotProperlySetted("The properties CommandId, ExecutionModality and User are not setted!!!");
        }
        GoodPrinciplesLoggerUtil.info(logger, this, "Inizio esecuzione comando");
        Method executionMethod = getTargetMethod(ExecutionMethod.class);
        try {
            executionMethod.invoke(this);
            GoodPrinciplesLoggerUtil.info(logger, this, "Fine esecuzione comando");
        } catch (RuntimeException ex) {
            GoodPrinciplesLoggerUtil.fatal(logger, this, "Unexpected error occurred", ex);
            throw new CommandRuntimeException("Unexpected error in command with ID "+getCommandInfo().getUuid());
        } catch (Exception ex) {
            GoodPrinciplesLoggerUtil.error(logger, this, "Error occurred in command", ex);
            throw new CommandException("Error occurred in command COMMAND_ID: "+getCommandInfo().getUuid());
        }
    }
    
    /* PRIVATE UTILITY METHODS */
    
    private boolean isTargetAnnotation(Annotation annotation) {
        if(ExecutionMethod.class.equals(annotation.annotationType()) && getCommandInfo().getExecutionModality().equals(((ExecutionMethod) annotation).executionModality())) {
            return true;
        } else if(CancelMethod.class.equals(annotation.annotationType()) && getCommandInfo().getExecutionModality().equals(((CancelMethod) annotation).executionModality())) {
            return true;
        }
        return false;
    }
    
    private Method getTargetMethod(Class<?> annotation) {
        if(annotation == null || (ExecutionMethod.class.equals(annotation) && CancelMethod.class.equals(annotation))) {
            GoodPrinciplesLoggerUtil.debug(logger, this, "Command not properly setted!!!!");
            throw new IllegalArgumentException(annotation+"????? What is this!!!");
        }
        Class<?> classUnderInspection = this.getClass();
        GoodPrinciplesLoggerUtil.debug(logger, this, "Class under inspection "+classUnderInspection+" for annotation "+annotation+"(executionModality="+getCommandInfo().getExecutionModality()+")");
        do {
            for(Method m: classUnderInspection.getDeclaredMethods()) {
                for(Annotation currentAnnotation: m.getDeclaredAnnotations()) {
                    if(currentAnnotation.annotationType().equals(annotation) && isTargetAnnotation(currentAnnotation)) {
                        GoodPrinciplesLoggerUtil.debug(logger, this, "executionMethod "+classUnderInspection+"."+m.getName());
                        return m; //trovato metodo da eseguire
                    }
                }
            }
        } while((classUnderInspection = classUnderInspection.getSuperclass()) != null);
        GoodPrinciplesLoggerUtil.debug(logger, this, "The current execution modality is not supported for class "+this.getClass());
        throw new CommandNotProperlySetted("The current execution modality is not supported for class "+this.getClass());
    }

}
